package srp6

import (
	"errors"
	"mangos/internal/binary"
	"mangos/internal/crypto/hash"
	"mangos/internal/math/big"
)

// User SRP6用户端
type User struct {
	username string                           // 用户名
	p        string                           // 明文密码
	N        *big.Int                         // SRP6 一个大安全素数（N=2q+1，q是一个素数）
	g        *big.Int                         // SRP6 一个以N为模的生成器
	s        *big.Int                         // SRP6 一个随机大数字
	A        *big.Int                         // SRP6 用户公钥
	a        *big.Int                         // SRP6 用户私钥
	B        *big.Int                         // SRP6 主机公钥
	K        *big.Int                         // SRP6 会话秘钥
	debug    func(format string, args ...any) // 调试日志输出
}

// Init 初始化用户端
func (user *User) Init(username string, p string, debug func(format string, args ...any)) {
	user.username = username
	user.p = p
	user.debug = debug

	// 生成用户私钥
	user.a = big.NewRand(19)
	user.debug("SRP6(用户端) %s: 用户私钥(a)=%s", username, user.a.Text(16))
}

// SetNgsB 设置 N,g,s,B 并返回 M,A
func (user *User) SetNgsB(
	N *big.Int,
	g *big.Int,
	s *big.Int,
	B *big.Int) (error, []byte, *big.Int) {
	user.N = N
	user.g = g
	user.s = s
	user.B = B

	user.debug("SRP6(用户端) %s: N=%s", user.username, user.N.Text(16))
	user.debug("SRP6(用户端) %s: g=%s", user.username, user.g.Text(16))
	user.debug("SRP6(用户端) %s: s=%s", user.username, user.s.Text(16))
	user.debug("SRP6(用户端) %s: 主机公钥(B)=%s", user.username, user.B.Text(16))

	if B.IsZero() {
		return errors.New("主机公钥(B)为零"), nil, nil
	}

	// A = g ^ a
	user.A = user.g.ModExp(user.a, user.N)
	user.debug("SRP6(用户端) %s: 用户公钥(A)=g^a=%s", user.username, user.A.Text(16))

	// u = H(A,B)
	sha1 := hash.Sha1Hash{}
	sha1.Init()
	sha1.Update(user.A.Binary(0))
	sha1.Update(user.B.Binary(0))
	sha1.Finalize()
	u := big.NewBinary(sha1.Digest())
	if u.IsZero() {
		return errors.New("扰动参数(u)为零"), nil, nil
	}
	user.debug("SRP6(用户端) %s: 扰动参数(u)=H(A,B)=%s", user.username, u.Text(16))

	// x = H(s,p)
	sha1.Init()
	sha1.Update(user.s.Binary(0))
	sha1.Update(binary.Flip(big.NewString(user.p, 16).Binary(0)))
	sha1.Finalize()
	x := big.NewBinary(sha1.Digest())
	user.debug("SRP6(用户端) %s: 私钥(x)=H(s,p)=%s", user.username, x.Text(16))

	// S = (B - kg^x) ^ (a + ux) (k=3)
	S := user.B.Sub(big.NewInt64(3).Mul(user.g.ModExp(x, user.N))).ModExp(user.a.Add(u.Mul(x)), user.N)
	user.debug("SRP6(用户端) %s: S=(B-kg^x)^(a+ux) (k=3)=%s", user.username, S.Text(16))

	// K = H(S)
	sha1.Init()
	sha1.Update(S.Binary(0))
	sha1.Finalize()
	user.K = big.NewBinary(CalcK(S))
	user.debug("SRP6(用户端) %s: 会话秘钥(K)=H(S)=%s", user.username, user.K.Text(16))

	// H(N) xor H(g)
	sha1.Init()
	sha1.Update(user.N.Binary(0))
	sha1.Finalize()
	hash1 := sha1.Digest()
	sha1.Init()
	sha1.Update(user.g.Binary(0))
	sha1.Finalize()
	hash2 := sha1.Digest()
	for i := 0; i < 20; i++ {
		hash1[i] ^= hash2[i]
	}

	// H(I)
	sha1.Init()
	sha1.Update([]byte(user.username))
	sha1.Finalize()
	I := sha1.Digest()

	// H(H(N) xor H(g), H(I), s, A, B, K)
	sha1.Init()
	sha1.Update(hash1)
	sha1.Update(I)
	sha1.Update(user.s.Binary(0))
	sha1.Update(user.A.Binary(0))
	sha1.Update(user.B.Binary(0))
	sha1.Update(user.K.Binary(0))
	sha1.Finalize()

	user.debug("SRP6(用户端) %s: M=H(H(N) xor H(g), H(I), s, A, B, K)=%s",
		user.username, binary.Text(sha1.Digest(), 16))

	return nil, sha1.Digest(), user.A
}
